查看原文
其他

秘籍:微服务设计的六脉神剑

2017-07-11 DevOps时代 DevOps时代

介绍

“微服务”是一种新的软件开发模式,它来源于提高软件开发和管理效率的一系列工程实践。敏捷方法、DevOps文化、PaaS、应用容器、CI/CD文化和技术的广泛采用,使得构建真正的模块化、大规模服务系统成为可能。

什么是微服务?

微服务是一种架构方法,强调将应用拆分成由跨职能团队管理的单目标、松散耦合的多个服务,以满足如今数字时代对软件系统交付和维护速率与质量的要求。

微服务与编程语言、平台、操作系统无关。它将庞大的应用拆分成更小更简单的应用,这些庞大的应用一般都是打成一个软件包。每个应用只需要做好一件事,微服务中的“微”就是指服务的功能范围,而不是指代码行数(LOC)少

每个应用都是由一个全栈团队构建,以减少不同团队之间潜在的沟通成本。微服务可能并不适合简单的应用,它更适合已经运行一段时间之后的复杂应用。

微服务背后的概念与面向对象的架构(SOA)很相似,所以有人叫这种架构为“SOA with DevOps”、“SOA for 潮人”、“SOA 2.0”

微服务的关键特点

  1. 领域驱动设计(DDD,Domain-Driven Design):采用Eric Evan的领域驱动设计可以很容易的实现功能的拆分

  2. 单一职责原则:每个服务只负责做好功能的一部分即可

  3. 显示地发布接口:生产者服务发布的接口一定有对应的消费者服务

  4. 独立地DURS(部署Deploy、更新Update、替换Replace、扩容Scale):每个服务都可以独立的部署、更新、替换和扩容

  5. 轻量级通信:服务之间的通信采用基于HTTP的REST、基于WebSocket的STOMP以及其他相似的轻量级协议

微服务的好处

1、独立扩容

每个微服务都可以根据需求通过横向扩容(增加CPU和内存)和纵向扩容(分片)实现独立扩容。这一点和因为非常不同的需求而要一起部署的大型应用不同

2、独立升级

每个微服务都可以独立的部署,开发者可以在不和其他团队协作的情况下轻松的完成针对一个服务的变更。比如:服务的性能可以通过改进底层实现而得到提升。独立升级提高了微服务的敏捷性,这也是CI/CD的基础。

3、易于维护

微服务的代码应该严格限制只实现一个功能,并且代码易于理解。这样IDE可以更容易的下载少量的代码,增加可读性也便于开发者保持效率。

4、潜在的异质和多语支持

开发者可以自由选择编程语言和技术栈开发微服务。这便于开发者使用更好的语言和技术重写服务,而不会受限于过去的决定。这让开发者在选择技术、工具、框架时更加自由。

5、错误和资源隔离

一个微服务出错时不影响其他的服务,而不像大型应用会导致整个程序故障,比如出现内存溢出和未关闭的数据库连接时。这就要求提高错误隔离并且限制应用失败的影响范围。

6、改进跨团队沟通

微服务应该由一支全栈团队构建。不同职能的同事在一支团队中一起工作,这样显著的提供了团队成员之间的沟通,因为团队的最终目标是一样的。

微服务的前提条件

微服务不是银弹,不能解决应用中的所有架构问题。实现微服务可能有所帮助,但这都是重构应用和按照微服务架构要求重写代码带来的副产品。真正的成功需要重大的投资。

1、服务复制

每个服务都应该可复制,典型的方式是横向克隆和纵向分区。应该有标准的机制使得服务应该可以轻易的基于元数据进行扩容,比如Red Hat的PaaS平台OpenShift。

2、服务发现

在微服务的世界里,服务 45 33246 45 14988 0 0 3290 0 0:00:10 0:00:04 0:00:06 3289是在PaaS环境中分布式运行的。不可变的基础设施由容器或者虚拟机镜像提供。服务可以根据预定义数据实现扩容和缩容。服务的真正地址需要在服务部署并且可以使用时才会确定。

服务终端地址动态特性可以使用服务注册和服务发现来解决。每个服务向代理进行注册并提供详细的信息,包括最终地址。其他的消费者服务查询代理并找出服务的地址并调用它。有很多方法可以注册和查找服务,比如ZooKeeper、etcd、consul、Kubernetes、Netflix Eureka等

3、服务监控

分布式系统中一个重要的部分就是服务监控和日志管理。这样可以在服务有问题时执行预防措施,比如一个服务访问了未授权的资源等。ELK技术栈可以收集不同服务中日志并且提供可视化展示。其他可以实现分布式日志管理的工具还有Syslog、Logentries和Loggly

4、容错性

无论如何进行测试,软件错误总会出现的。在基于多种微服务的分布式系统中容错性更加重要。核心理念不是“如何避免错误”而是“如何处理错误”。微服务自动的采取行动以避免对用户体验造成影响非常重要。断路器模式允许在软件中构建容错性。Netflix的Hystrix和Ribbon都是该模式很好的实现库。

5、DevOps

对基于微服务的应用而言,持续集成和持续部署至关重要。这些实践可以更早识别错误,可以减少在构建不同的服务时不同团队之间的协作。

大型应用的优秀设计原则

将大型应用重构为微服务的应用不能解决所有的架构问题。在拆分大型应用之前,我们首先应该确认大型应用是按照如下软件架构原则设计的。

  1. 实践内容分离,比如使用MVC

  2. 高内聚、低耦合的API

  3. 不重复(DRY)

  4. 惯例由于配置(CoC)

  5. 界面和实现分离,遵循迪米特法则,类不能直接调用其他类,应该通过第三方完成

  6. 使用领域驱动设计保证一个领域/组件的对象在一起

  7. 不过度设计

重构大型应用为微服务架构

一般的Java EE大型应用都是只有一个WAR包或者EAR包,所有的功能都打包到一个包里。比如:一个购物车可能包括用户、商品、订单功能。所有的web页面都在应用的根目录,相关的Java类都在WEB-INF/classes目录下,所有的资源文件都在WEB-INF/classes/META-INF目录下。

图1:大型架构

这样的应用可以重构成微服务,架构如下:

图2:重构成微服务

  1. 将应用功能分解成用户、订单、商品组件,打包到不同的WAR包中。每一个WAR包都包含了该组件需要的网页、类和配置文件

  2. 使用Java EE实现每个组件,因为组件之间依靠良好定义的API通信

  3. 组件中的不同类属于同一个领域,代码易于编写和维护,技术债务很低,底层的技术栈也可以变更

  4. 每个包都有自己的数据库(即数据存储不是共享的)。这样每个微服务就可以选择任何的数据库存储技术,关系型数据库、NoSQL、文件、内存等

  5. 每个组件都向代理进行注册,之所以这样是因为无状态的服务只在需要的时候提供使用,而真正的最终地址只有在运行时才会确定。Netflix的Eureka、etcd和Zookeeper都可以试下服务注册和服务发现

  6. 当服务之间需要通信时,可以采用API.REST进行同步通信或者Pub/Sub进行异步通信。在例子中,订单组件通过REST API和用户服务、商品服务通信

  7. 客户界面是在另一个应用中定义的(例子中是Shopping Cart UI)。这个应用从代理中发下服务并且将服务组合起来。这个应用一般都是一个代理,只负责显示每个组件的UI。可以通过提供标准的CSS和JavaScript资源实现UI。

更多内容参见:https://github.com/arun-gupta/microservices

微服务设计模式

多个微服务可以组合对外提供服务,如下提供一些常见的微服务设计模式:

1、聚合模式

将多个微服务聚合成一个组合服务

在最简单的情况下,聚合器就是一个简单的网页,负责调用多个服务以实现应用的功能。因为每一个服务(服务A、服务B、服务C)对外暴露了轻量级的REST机制,网页可以获取并展示反回的数据。当需要流程时,可以将数据进行转换之后再在聚合器网页中展示。比如需要对单个服务返回的数据执行业务逻辑时。

图3:聚合模式

聚合器也可以是更高级别的组合微服务,供其他服务调用。在例子中,聚合器会从每个独立的微服务中收集数据,执行业务逻辑,然后以REST 的形式推送到终端。

这种设计模式是基于DRY原则,如果多个服务都需要访问服务A\B\C,则可以在逻辑上的抽象出一个组合服务,将业务逻辑都聚合在该组合服务中。这种模式的好处在于单个服务(服务A\B\C)可以独立的进化。业务逻辑依然由组合服务来提供。

2、代理模式

代理服务设计模式是聚合模式的变种。这种情况下,聚合不用发生在客户端一侧,根据业务逻辑会调用一个不同的服务。

如同聚合模式一样,代理可以独立的横向和纵向扩容。当单个服务不再需要暴露给消费服务,而是直接提供给界面使用时,这点尤为重要

图4:代理模式

代理模式有两种:

一种是dumb proxy,这种代理只是将所有的请求委托给某个服务;

另一种是smart proxy,这种代理在响应客户端请求之前会执行数据转换。举个例子,不同的设备中展现层包含在一个smart proxy中

3、链式模式

链式模式会明确一个统一的入口,在例子中,客户端请求服务A,服务A再请求服务B,服务B再请求服务C。所有服务都使用同步的HTTP请求和响应信息

图5:链式模式

客户端会阻塞直到链式服务都响应完成(服务A<—>服务B<—>服务C)。A与B之间,B与C之间的请求和响应可能完成不一样。相当于每一个服务在不断增加自己的功能与价值。

由于同步调用的限制,不能进行过长的链式调用,会导致客户端的响应超长。之后的模式中会介绍如何解决这种阻塞。

:单例链式只有一个服务的链式模式

4、分支模式

分支模式扩展了聚合模式,允许同时调用两个链式服务。这种模式能应用于调用不同链式服务或单例链式服务。

图6:分支模式

服务A可以是网页或者组合服务,同时调用两条链式服务,类似聚合模式。服务A也可以调用单例链式服务。

5、共享模式

微服务的设计原则之一就是自治。即服务是是全栈的,可以控制组件的一切资源:UI、中间件、持久层、数据处理等。这就允许服务可以是多语言的,正确的工具用于完成正确的工作。比如,应用中有些数据适合图形存储,有些适合关系型数据存储,则可以根据领域选择适合的存储。而不是将所有数据都塞进SQL数据库或者NoSQL数据库。

但是,一个典型的问题(尤其是当重构大型应用时)是数据库的正常化:保证每个微服务都有正确的数据,不多不少。即便是大型应用只用了一个SQL数据库,非规范化数据库会导致数据重复,可能造成数据不一致。在数据处理阶段,一些应用会受益于共享数据微服务设计模式

图7:共享模式

一些微服务,比如链式服务,在两个服务关系紧密时,可以共享缓存和数据库存储。可能会有人说这是一种反模式,但是业务逻辑需求可能会有这种情况。

6、消息模式

如今REST设计模式非常流行,但是它在异步机制上先天不足,容易导致阻塞。为了实现异步,一些微服务架构选择使用消息队列代理REST请求/响应

图8:消息模式

如图所示的模式中,服务A可能同步调用服务C,服务C使用共享的消息队列异步的调用服务B和服务D。服务A和C之间也可以是异步的。比如使用WebSocket实现扩展性

可以根据业务需求组合使用REST和发布订阅模式

结束语

微服务可以帮助业务快速的进化。但是大型应用目前依然很没有问题,之后的很多年也将继续使用。在重构你的大型应用之前请考虑微服务的前提条件以保证收益。很多时候,好的软件工程和架构已经足够了。但是如果你决定采用微服务,本文应该能帮到你。

链接:https://dzone.com/storage/assets/293353-rc215-microservices.pdf


了解更多微服务与 DevOps 的玩儿法,来DevOpsDays上海站吧

王磊,顾宇,许进三位老师将为您带来精彩的微服务专场演讲


点击“阅读原文”,关注 DevOpsDays上海站


您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存